// https://www.lodashjs.com/docs/lodash.cloneDeep#_clonedeepvalue
// https://blog.csdn.net/qq_25257229/article/details/117969685
// https://jishuin.proginn.com/p/763bfbd65bed
// 深拷贝实现方式

// 1.JSON.parse(JSON.stringify()) ,它能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象，即那些能够被 json 直接表示的数据结构，不能拷贝函数，Date，RegExp, Symbol,原型链等
// 不支持函数
// 不支持undefined（支持null）
// 不支持循环引用，比如 a = {name: 'a'}; a.self = a; a2 = JSON.parse(JSON.stringify(a))
// 不支持Date，会变成 ISO8601 格式的字符串
// 不支持正则表达式
// 不支持Symbol
const target = {
  name: "wety",
  age: 234,
  gender: "male",
  friends: [{ name: "daf" }],
  sayname: function () {},
  date: new Date(),
  id: Symbol(),
  exp: new RegExp("agb"),
  m: new Map(),
};
console.log(JSON.parse(JSON.stringify(target)));
// { name: 'wety', age: 234, gender: 'male', friends: [ { name: 'daf' } ], date: '2022-01-06T01:21:44.340Z' }

// 2.jquery.extend()  $.extend(true,{},a);  // 循环引用的bug
var objA = {};
var objB = {};
objA.b = objB;
objB.a = objA;
// $.extend(true, {}, a); // Uncaught RangeError: Maximum call stack size exceeded(…)

// 3.简易版
/**
 * 深克隆（深拷贝）+ 解决深拷贝函数中循环引用时导致的栈溢出的问题
 * @param {object} origin
 * @param {*} hashMap WeakMap 数据，用于缓存克隆过的对象
 * @returns origin / 克隆的origin
 * 
 * WeakMap:
 * 1、【特殊点】WeakMap只接受引用类型（对象）作为键名；
   2、【优点】WeakMap的键名所指向的对象都是弱引用，不计入垃圾回收机制，不用考虑内存泄漏。 当引用对象被清除，其所对应的WeakMap记录就会自动被移除。（具体请看ES6相关解释，这里不展开。）
 */
const deepClone = function (target, hashMap) {
  let result = Array.isArray(target) ? [] : {};
  hashMap = hashMap || new WeakMap();
  if (hashMap.get(target)) {
    // 查缓存字典中是否已有需要克隆的对象，有的话直接返回同一个对象（同一个引用，不用递归无限创建进而导致栈溢出了）
    return hashMap.get(target);
  }
  if (typeof target === "object" && target !== null) {
    hashMap.set(target, result); // 哈希表缓存新值
    // if (Array.isArray(target)) {
    if (Object.prototype.toString.call(target) === "[object Array]") {
      // 遍历数组
      target.forEach((item) => {
        result.push(deepClone(item, hashMap)); // 递归
      });
    } else {
      // 克隆对象
      for (const key in target) {
        if (Object.hasOwnProperty.call(target, key)) {
          const value = target[key];
          result[key] = deepClone(value, hashMap);
        }
      }
    }
  } else {
    return target;
  }
  return result;
};
var refer1 = {
  arr: [1, 2, 3],
};
refer1.refer = refer1;
console.log("deepClone refer1-----------", deepClone(refer1));
console.log("deepClone target-----------", deepClone(target));

// 4
const clone = (function () {
  // 获取对象类型，返回值 "Object Date RegExp Array Map..."
  const classof = function (o) {
    if (o === null) {
      return null;
    }
    if (o === undefined) {
      return undefined;
    }
    const type = Object.prototype.toString.call(o).split(" ").pop();
    return type;
  };

  // 处理循环引用的问题
  let renference = null;

  // 处理不同类型的对象
  const handlers = {
    // 处理正则
    RegExp: function (reg) {
      let flag = "";
      flag += reg.global ? "g" : "";
      flag += reg.multiline ? "m" : "";
      flag += reg.ignoreCase ? "i" : "";
      return new RegExp(reg.source, flag);
    },
    // 处理Date对象
    Date: function (date) {
      return new Date(+date);
    },
    // 处理数组
    Array: function (arr) {
      let newArr = [];
      for (let i = 0; i < arr.length; i++) {
        const item = arr[i];
        if (renference.indexOf(item) > -1) {
          continue;
        }
        const handler = handlers[classof(item)];
        if (handler) {
          renference.push(item);
        }
        newArr[i] = item;
      }
      return newArr;
    },
    // 处理对象
    Object: function (obj) {
      let newObj = {};
      for (const key in obj) {
        if (Object.hasOwnProperty.call(obj, key)) {
          const value = obj[key];
          if (renference.indexOf(value) > -1) {
            continue;
          }
          const handler = handlers[classof[value]];
          if (handler) {
            renference.push(value);
          }
          newObj[key] = value;
        }
      }
      return newObj;
    },
  };

  return function (obj) {
    renference = [];
    const handler = handlers[classof(obj)];
    return handler ? handler(obj) : obj;
  };
})();
// 测试
console.log(clone(target));

// 5.lodash  https://www.lodashjs.com/docs/lodash.cloneDeep#_clonedeepvalue
